
import asyncore
import asynchat
import sys
import thread

from coffeetalk.protocol import ioml
from coffeetalk.utilities import iologger
from coffeetalk.server import iouser

class IOAsyncConnection(asynchat.async_chat):

    BUFFER_SIZE     = 1 * 1024
    MAX_BUFFER      = 128 * 1024
    
    def __init__(self,sock,ip,map,userclass,iotrees,log):
        
        self.ac_in_buffer_size = IOAsyncConnection.BUFFER_SIZE
        self.ac_out_buffer_size = IOAsyncConnection.BUFFER_SIZE
        
        self.ac_in_buffer = ''
        self.ac_out_buffer = ''
        self.producer_fifo = asynchat.fifo()
        self.map = map
        asyncore.dispatcher.__init__(self,sock=sock, map=self.map)
        
        self.ip = ip
        self.log = log
        
        self.set_terminator(ioml.TERMINATOR)
        self.bufferincoming = ''
        self.bufferoutgoing = ''
        self.lastping = 0
        
        self.nullbyte = False
        self.outgoinglock = thread.allocate_lock()
        self.user = iouser.IOUser(self,iotrees,userclass=userclass)
    
    def main_loop(self,timeout):
        asyncore.loop(timeout=timeout,use_poll=True,map=self.map)
        
    def collect_incoming_data(self,data):
        self.bufferincoming = ''.join((self.bufferincoming,data))
        if len(self.bufferincoming) > IOAsyncConnection.MAX_BUFFER:
            try:
                self.close_when_done()
                self.push("")
            except: pass        
    
    def found_terminator(self):
        self.lastping = 0
        if self.nullbyte:
            if self.bufferincoming.find('\0') == 0:
                self.bufferincoming = self.bufferincoming[1:]
        #nullindex = self.bufferincoming.find('\0')
        #print 'null index: ' + str(nullindex)
        self.user.message_handler(self.bufferincoming + self.get_terminator())
        self.bufferincoming = ''
        
    def send_message(self,msg):
        self.outgoinglock.acquire()
        self.bufferoutgoing = ''.join((self.bufferoutgoing,msg))
        if self.nullbyte:
            self.bufferoutgoing = ''.join((self.bufferoutgoing,'\0'))
        self.outgoinglock.release()
    
    def readable(self):
        self.lastping = self.lastping + 1
        if self.user.status == iouser.IOUser.CONNECTING:
            if self.lastping > 15:
                self.handle_close(reason='Login timeout')
        else:
            if self.lastping > 60:
                pass        

        return True
    
    def writable(self):
        self.outgoinglock.acquire()
        bufferlength = len(self.bufferoutgoing)
        self.outgoinglock.release()
        return (bufferlength > 0)

    def handle_write(self):
        self.outgoinglock.acquire()
        sent = self.send(self.bufferoutgoing)
        self.bufferoutgoing = self.bufferoutgoing[sent:]
        self.outgoinglock.release()
            
    def handle_close(self,reason=None):
        
        if reason is not None:
            self.log.log_error(iologger.DEBUG,'0.0.0.0',self.user.username,self.user.group,iologger.current_function(),'OK',str(reason))
        
        try:
            self.sendall(self.bufferoutgoing)
        except: pass
        try:
            self.user.close()
        except: 
            type,value = sys.exc_info()[:2]
            self.log.log_error(iologger.DEBUG,'0.0.0.0',self.user.username,self.user.group,iologger.current_function(),'InternalError',str(type) + ':' + str(value))
        try:
            self.close()
        except:
            type,value = sys.exc_info()[:2]
            self.log.log_error(iologger.DEBUG,'0.0.0.0',self.user.username,self.user.group,iologger.current_function(),'InternalError',str(type) + ':' + str(value))
                          
    def handle_error(self):
        type,value = sys.exc_info()[:2]
        self.log.log_error(iologger.ERROR,self.ip,self.user.username,self.user.group,iologger.current_function(depth=1),'InternalError',str(type) + ':' + str(value))
        self.handle_close()
        
    def handle_expt(self):
        pass
    